function skinsGadget(moduleId)
{
    this.prefs = new Object();
    this.miniMsg = new Object();
    this.tabs = null;
    this.skinData = new Object();
    this.skinDataHash = new Object();
    this.criteria = new Array();
    this.origCss = "";
    this.skinName = "";
    this.isIE = false;
    this.testSkin = "";
    this.schedule = "";
    this.ruleId = 0;
    this.moduleId = moduleId;
    this.isDirty = false;
    this.skinChoice = 0;

    this.init = function()
    {
        this.GADGET_VERSION = "2.5";

        this.prefs = new _IG_Prefs(this.moduleId);
        this.miniMsg = new _IG_MiniMessage(this.moduleId, _gel("minimsgs" + this.moduleId));
        this.cssUrl = decodeURIComponent(this.prefs.getString("cssUrl"));
        this.presetSkinCss = decodeURIComponent(this.prefs.getString("presetSkinCss"));
        this.presetTestSkin = this.prefs.getString("presetTestSkin");
        this.presetName = this.prefs.getString("presetSkinName");
        this.presetSchedule = this.prefs.getString("presetSchedule");
        this.storedCssUrl = decodeURIComponent(this.prefs.getString("cssUrl"));
        this.origCss = decodeURIComponent(this.prefs.getString("origCss"));
        this.skinName = this.prefs.getString("skinName");
        this.testSkin = this.prefs.getString("testSkin");
        this.readUpdate = this.prefs.getString("readUpdate9");
        this.showUpdates = this.prefs.getBool("showUpdates");
        this.schedule = decodeURIComponent(this.prefs.getString("schedule"));
        this.isScheduleMode = this.prefs.getBool("isScheduleMode");
        this.adSupported = this.prefs.getBool("adSupported");
        this.hiddenMode = this.prefs.getBool("hiddenMode");
        this.showDirectory = this.prefs.getBool("showDirectory");
        this.showSchedule = this.prefs.getBool("showSchedule");
        this.showCreate = this.prefs.getBool("showCreate");
        this.showSubmit = this.prefs.getBool("showSubmit");
        this.showNews = this.prefs.getBool("showNews");
        this.showAuthorUrls = this.prefs.getBool("showAuthorUrls");
        this.skinChoice = this.prefs.getInt("skinChoice");
        this.flickrGroupId = this.prefs.getString("flickrGroupId");
        this.loadingImage = false;

        this.isIE = document.all;
        this.saveId = 0;
        this.useCache = true;
        this.skinChanges = 0;
        this.orderBy = this.prefs.getInt("orderby");
        this.directoryUrl = "http://bonstio.net/igskins/getSkins.php";
        this.baseCssUrl = "http://igskins.googlecode.com/svn/trunk/css/base.css";
        this.scheduleCss = "";
        this.flickrResponse = null;
        this.testing = "TESTING";
        this.feelingLucky = "Feeling Lucky?";
        this.feelingLuckyUrl = "http://igskins.googlecode.com/svn/trunk/skins/random/random.css";
        this.bgImage = new Image();
        this.criteria = [
                ["Default", "by default", null],
                ["Time of day", "until", this.getHours()],
                ["Time of day", "after", this.getHours()],
                ["Weather", "if the weather is", ["clear","partly sunny","mostly sunny", "sunny", "cloudy","chance of rain","rain", "chance of storm", "storm", "chance of snow", "snow", "fog"]],
                ["Day","if today is",this.getDoW()],
                ["GMail","if my unread email count <",["[Feature coming soon]","1","2", "5", "10", "50", "100"]],
                ["IP", "if my IP address matches", []]
                ];

        this.googleSkins = [
                ["Holiday Village","http://img0.gmodules.com/ig/images/skins/holidayvillage.jpg","http://img0.gmodules.com/ig/f//skins/holidayvillage/holiday_village_" + this.getHoursOfDay(2) + ".css"],
                ["Beach","http://img0.gmodules.com/ig/images/skins/beach.png","http://www.google.com/ig/f//skins/beach/beach_" + this.getHoursOfDay(2) + ".css"],
                ["Aja Tiger","http://img1.gmodules.com/ig/images/skins/tiger.png","http://www.google.com//ig/f//skins/tiger/tiger_" + this.getHoursOfDay(2) + ".css"],
        //["Solar System","http://img2.gmodules.com/ig/images/skins/planets.png",""],
                ["Hong Kong","http://img2.gmodules.com/ig/images/skins/hongkong.png","http://www.google.com/ig/f//skins/hongkong/hongkong_" + this.getHoursOfDay(2) + ".css"],
                ["Tea House","http://img0.gmodules.com/ig/images/skins/teahouse.png","http://www.google.com/ig/f//skins/teahouse/teahouse_" + this.getHoursOfDay(2) + ".css"],
                ["Seasonal Scape","http://img0.gmodules.com/ig/images/skins/springscape.png","http://www.google.com/ig/f//skins/springscape/springscape_" + this.getHoursOfDay(2) + ".css"],
                ["City Scape","http://img1.gmodules.com/ig/images/skins/city.png","http://www.google.com/ig/f//skins/cityscape/cityscape_" + this.getHoursOfDay(2) + ".css"],
                ["Autumn","http://img1.gmodules.com/ig/images/skins/fallscape.png","http://www.google.com/ig/f//skins/autumn/autumn_" + this.getHoursOfDay(2) + ".css"],
                ["Winter Scape","http://img0.gmodules.com/ig/images/skins/winterscape.png","http://www.google.com/ig/f//skins/winterscape/winterscape_" + this.getHoursOfDay(2) + ".css"],
                ["Sweet Dreams","http://img2.gmodules.com/ig/images/skins/sweetdreams.png","http://www.google.com/ig/f//skins/sweetdreams/sweetdreams_" + this.getHoursOfDay(2) + ".css"],
                ["JR","http://img2.gmodules.com/ig/images/skins/JR.png","http://www.google.com/ig/f//skins/JR/JR_" + this.getHoursOfDay(2) + ".css"],
                ["Skateboard","http://img2.gmodules.com/ig/images/skins/skateboard.png","http://www.google.com/ig/f//skins/skateboard.css"]
        //["Bus Stop","http://img1.gmodules.com/ig/images/skins/busstop.png",""]
                ];

        if (this.hiddenMode)
        {
            var thisModule = _gel("m_" + this.moduleId);
            thisModule.style.display = "none";
            var addStuff = _gel("addstuff");
            var sSpan = document.createElement("span");
            sSpan.setAttribute("id", "sSpan" + this.moduleId);
            var aHref = document.createElement("a");
            var space = document.createTextNode(" | ");
            aHref.href = "javascript:void(0);";
            aHref.innerHTML = "Custom skins"
            sSpan.appendChild(aHref);
            sSpan.appendChild(space);
            aHref.onclick = this.createMethodReference(this, "toggleHidden");

            addStuff.insertBefore(sSpan, addStuff.lastChild);
            _gel("hide" + this.moduleId).style.display = "block";
        }

        //Show Ad
        if (this.adSupported == 1)
        {
            var now = new Date().getTime();
            var lastAdShow = this.prefs.getInt("LAST_AD_SHOW");
            if (lastAdShow != "")
            {
                //Every 4 days
                var interval = 1000 * 60 * 60 * 24 * 4;

                if ((lastAdShow + interval) < now)
                {
                    this.showAd();
                    this.prefs.set("LAST_AD_SHOW", now);
                }
            }
            else
            {
                this.prefs.set("LAST_AD_SHOW", now);
            }
        }

        //store old skin
        var styleSheets = document.getElementsByTagName("link");

        for (var i = 0; i < styleSheets.length; i++)
        {
            if ((styleSheets[i].href.indexOf("skins") >= 0) || (styleSheets[i].href.indexOf("ig.css") >= 0))
            {
                this.origCss = styleSheets[i].href;
                this.prefs.set("origCss", encodeURIComponent(styleSheets[i].href));
                break;
            }
        }

        this.addCustomStyle("igBase", this.baseCssUrl, true);

        //Retrieve the skins and set the active one
        this.getSkins();
    };

    this.renderTabs = function(skinGadget)
    {
        this.tabs = new _IG_Tabs(this.moduleId, "Directory", _gel("tabContainer" + this.moduleId));

        //Sort out tabs
        this.tabs.alignTabs("left", "3");

        if (this.showDirectory)
        {
            _gel("directory" + this.moduleId).style.display = "block";
            this.tabs.addTab(skinGadget.prefs.getMsg("tabDirectory"), {
                contentContainer: _gel("select" + this.moduleId),
                callback: _IG_Callback(this.showSortBy, true, this),
                tooltip: skinGadget.prefs.getMsg("tabDirectoryHover")
            });
        }

        if (this.showSchedule)
        {
            this.tabs.addTab(skinGadget.prefs.getMsg("tabSchedule"), {
                contentContainer: _gel("schedule" + this.moduleId),
                callback: _IG_Callback(this.showSortBy, true, this),
                tooltip: skinGadget.prefs.getMsg("tabScheduleHover")
            });
        }

        if (this.showCreate)
        {
            this.tabs.addTab(skinGadget.prefs.getMsg("tabCreate"), {
                contentContainer: _gel("create" + this.moduleId),
                callback: _IG_Callback(this.showSortBy, false, this),
                tooltip: skinGadget.prefs.getMsg("tabCreateHover")
            });
        }

        if (this.showSubmit)
        {
            this.tabs.addTab(skinGadget.prefs.getMsg("tabSubmit"), {
                contentContainer: _gel("submit" + this.moduleId),
                callback: _IG_Callback(this.showSortBy, false, this),
                tooltip: skinGadget.prefs.getMsg("tabSubmitHover")
            });
        }

        if (this.showNews)
        {
            this.tabs.addTab(skinGadget.prefs.getMsg("tabUpdates"), {
                contentContainer: _gel("updates" + this.moduleId),
                callback: _IG_Callback(this.showSortBy, false, this),
                tooltip: skinGadget.prefs.getMsg("tabUpdatesHover")
            });
        }

        //Show updates as required
        if ((this.showUpdates) && (this.readUpdate == ""))
        {

            var readmore = 'read more by enabling the news tab';
            if (skinGadget.showUpdates)
            {
                var newsTabId = skinGadget.getTabId(skinGadget.prefs.getMsg("tabUpdates"), skinGadget);
                readmore = '<a href="javascript:void(0)" onclick="this.blur(); skinGadget' + skinGadget.moduleId
                        + '.tabs.setSelectedTab(' + newsTabId + ');">read more on the news tab</a>';
            }
            var miniMsg = this.miniMsg.createDismissibleMessage(skinGadget.prefs.getMsg("update") + ' - ' + readmore
                    + ' or <a href="javascript:void(0)" onclick="this.blur(); skinGadget' + this.moduleId
                    + '.optOut();"> never display these updates</a>', function()
            {
                skinGadget.prefs.set("readUpdate9", "true");
            });
            miniMsg.style.margin = "1px";
            miniMsg.style.color = "#000000";
        }

        skinGadget.getCreateInstructions(2, skinGadget);
        skinGadget.getUpdates(3, skinGadget);
        skinGadget.getSubmitInstructions(4, skinGadget);
    };

    this.getSkins = function()
    {
        var orderby = "";

        if (this.orderBy == 0)
        {
            orderby = "?popular=1";
        }
        else if (this.orderBy == 2)
        {
            orderby = "?popular=3"
        }

        var rInterval = this.testSkin == "" ? 60 * 60 : 0;
        _IG_FetchContent(this.directoryUrl
                + orderby, _IG_Callback(this.onGotSkins, this), { refreshInterval: rInterval });
    };

    this.getCreateInstructions = function(tabId, skinGadget)
    {
        _gel("createInner" + skinGadget.moduleId).innerHTML = skinGadget.prefs.getMsg("createInstructions");
    };

    this.getSubmitInstructions = function(tabId, skinGadget)
    {
        _gel("submitInstructions" + skinGadget.moduleId).innerHTML = skinGadget.prefs.getMsg("submitInstructions");
    };

    this.getUpdates = function(tabId, skinGadget)
    {
        _gel("updatesInner" + skinGadget.moduleId).innerHTML = skinGadget.prefs.getMsg("updates");
    };

    this.getControl = function(sourceArray, selector, valueSelector)
    {
        selector = selector ? selector : "";
        valueSelector = valueSelector ? valueSelector : "i";

        var aControl = document.createElement("select");
        aControl.style.fontSize = "90%";

        if (sourceArray == null)
        {
            aControl.style.display = "none";
        }
        else if (sourceArray.length == 0)
        {
            aControl = document.createElement("input");
            aControl.setAttribute("type", "text");
            aControl.setAttribute("maxlength", "15");
            aControl.setAttribute("size", "15");
            aControl.style.fontSize = "90%";
            aControl.onchange = function()
            {
            };
            if (ipaddr)
            {
                aControl.value = ipaddr;
            }
        }
        else
        {
            for (var i = 0; i < sourceArray.length; i++)
            {
                var expr = "aControl.options[aControl.options.length] = new Option(sourceArray[i]" + selector + ", "
                        + valueSelector + ");";
                eval(expr);
            }
        }

        return aControl;
    };

    this.showSortBy = function (sched, show, skinGadget)
    {
        document.getElementById("sortBy" + skinGadget.moduleId).style.display = show ? "block" : "none";
    };

    this.getSchedule = function(tabId, skinGadget)
    {
        var tbl = document.createElement("table");
        var cDiv = document.createElement("div");
        var tblBody = document.createElement("tbody");
        var iTblBody = document.createElement("tbody");
        var tblHeader = document.createElement("thead");
        var iTblHeader = document.createElement("thead");
        var row = document.createElement("tr");
        var cell1 = document.createElement("td");
        var use = document.createTextNode("Use");
        var cell2 = document.createElement("td");
        var add = document.createElement("img");
        var skins = skinGadget.getControl(skinGadget.skinData, ".name", "sourceArray[i].id");
        var condition = skinGadget.getControl(skinGadget.criteria, "[1]");
        var conditionValue = skinGadget.getControl(skinGadget.criteria[0][2], null);
        var addLink = document.createElement("a");
        var addLinkDiv = document.createElement("div");
        var container = _gel("schedule" + skinGadget.moduleId);
        var schedInstr = document.createElement("div");
        var criteriaDiv = document.createElement("div");
        var iTable = document.createElement("table");
        var itr = document.createElement("tr");
        var itd = document.createElement("td");
        var itd2 = document.createElement("td");

        tbl.setAttribute("id", "cTable" + skinGadget.moduleId);
        tbl.style.textAlign = "left";
        tbl.style.lineHeight = "2";
        tbl.style.width = "99%";
        tbl.cellSpacing = "0px";

        cDiv.setAttribute("id", "cDiv" + skinGadget.moduleId);
        tblHeader.style.textAlign = "center";
        tblBody.setAttribute("id", "rules" + skinGadget.moduleId);
        cell2.vAlign = "middle";
        cell1.colSpan = "2";
        add.setAttribute("src", skinGadget.getUrl("http://igskins.googlecode.com/svn/trunk/images/add.png"));
        add.style.border = "0px";
        add.title = skinGadget.prefs.getMsg("tabScheduleAddHover");
        add.style.verticalAlign = "middle";
        skins.setAttribute("id", "skinsRule" + skinGadget.moduleId);

        //criteriaDiv.style.width = "100px";
        iTable.appendChild(iTblHeader);
        iTable.style.width = "99%";
        itd.style.textAlign = "center";
        itd.style.textAlign = "right";
        itd.vAlign = "top";
        itd.style.width = "15%"
        itd.appendChild(use);
        itr.appendChild(itd);
        itr.appendChild(itd2);
        iTblBody.appendChild(itr);
        iTable.appendChild(iTblBody);
        criteriaDiv.appendChild(iTable);

        condition.onchange = function ()
        {
            skinGadget.conditionChanged(condition.selectedIndex);
        };
        conditionValue.setAttribute("id", "conditionValue" + skinGadget.moduleId);

        addLink.setAttribute("href", "javascript:void(0)");
        addLink.onclick = function()
        {
            this.blur();
            var aControl = _gel("conditionValue" + skinGadget.moduleId);
            var cV = aControl.selectedIndex;
            var aVal = aControl.value;
            skinGadget.addRule([skins.options[skins.selectedIndex].value, condition.selectedIndex, cV, aVal], skinGadget, true);
        };
        addLink.appendChild(add);
        addLinkDiv.style.display = addLinkDiv.style.display ? "table-cell" : "block";
        addLinkDiv.style.verticalAlign = "middle";
        addLinkDiv.appendChild(addLink);
        addLinkDiv.appendChild(addLink);

        cell1.appendChild(criteriaDiv);
        itd2.appendChild(skins);
        itd2.appendChild(document.createTextNode(" "));
        itd2.appendChild(condition);
        itd2.appendChild(document.createTextNode(" "));
        itd2.appendChild(conditionValue);
        itd2.appendChild(document.createTextNode(" "));
        cell2.appendChild(addLinkDiv);

        row.appendChild(cell1);
        row.appendChild(cell2);

        tblHeader.appendChild(row);
        tbl.appendChild(tblHeader);
        tbl.appendChild(tblBody);
        tbl.appendChild(tblBody);

        schedInstr.className = "bottomMargin" + skinGadget.moduleId;
        schedInstr.innerHTML = skinGadget.prefs.getMsg("scheduleInstructions");

        container.innerHTML = "";
        container.appendChild(schedInstr);
        container.appendChild(tbl);

        //Setup the current schedule
        var rows = skinGadget.schedule.split("$$");
        for (var i = 0; i < rows.length; i++)
        {
            row = rows[i].split("|");
            if (row.length > 2)
            {
                skinGadget.addRule(row, skinGadget, false)
            }
        }

        if (skinGadget.isScheduleMode)
        {
            skinGadget.computeAndSetSkinFromSchedule(skinGadget);
        }
    };

    this.conditionChanged = function(selectedIndex)
    {
        var select = _gel("conditionValue" + this.moduleId);
        var parent = select.parentNode;
        select.style.display = "none";
        var dropDown = this.getControl(this.criteria[selectedIndex][2]);
        dropDown.setAttribute("id", "conditionValue" + this.moduleId);
        parent.insertBefore(dropDown, select);
        parent.removeChild(select);
    };

    this.saveRules = function()
    {
        //save the current schedule rules
        var rulesString = "";
        var rulesStringArr;
        var rules = _gel("rules" + this.moduleId);
        var limit = rules.childNodes.length;

        for (var i = 0; i < limit; i++)
        {
            var row = rules.childNodes[i];
            if (row.style.display != "none")
            {
                rulesStringArr = row.firstChild.id.split("|");
                //rulesStringArr[rulesStringArr.length - 1] = null;
                rulesString += rulesStringArr.join("|") + "$$";
            }
        }

        this.schedule = rulesString;
        this.save();
    };

    this.getSkinFromRules = function()
    {
        //save the current schedule rules
        var skin = "";
        var rules = _gel("rules" + this.moduleId);
        var limit = rules.childNodes.length;
        var rulesStringArr, rulesString;

        for (var i = 0; i < limit; i++)
        {
            var row = rules.childNodes[i];
            if (row.style.display != "none")
            {
                rulesStringArr = row.firstChild.id.split("|");
                rulesStringArr[rulesStringArr.length - 1] = null;
                rulesString += rulesStringArr.join("|") + "$$";
            }
        }

        this.schedule = rulesString;
        this.save();
    };

    this.addRule = function(ruleDetails, skinGadget, save)
    {
        var skinIndex = ruleDetails[0];
        var item = skinGadget.skinDataHash[skinIndex];

        if (item)
        {
            var conditionIndex = ruleDetails[1];
            var conditionValueIndex = ruleDetails[2] == undefined ? "" : ruleDetails[2];
            var conditionValueSet = ruleDetails[3];
            var rules = _gel("rules" + skinGadget.moduleId);
            var row = document.createElement("tr");
            var cell1 = document.createElement("td");
            var cell2 = document.createElement("td");
            var cell3 = document.createElement("td");
            var remove = document.createElement("img");
            var removeLink = document.createElement("a");
            var removeLinkHolder = document.createElement("div");
            row.style.cursor = "move";
            var thumbUrl = skinGadget.getImageUrl(item.thumbnail);
            var title = item.name + ' by ' + item.author;

            row.setAttribute("id", "ruleId" + skinGadget.moduleId + skinGadget.ruleId++);
            var cell1id = skinIndex + "|" + conditionIndex + "|" + conditionValueIndex;
            if (conditionValueIndex == "")
            {
                cell1id = cell1id + "|" + conditionValueSet;
            }
            cell1.setAttribute("id", cell1id);
            cell1.style.padding = "4px";
            cell1.style.width = "80px";
            cell1.vAlign = "middle";
            remove.setAttribute("src", skinGadget.getUrl("http://igskins.googlecode.com/svn/trunk/images/cross.png"));
            remove.style.border = "0px";
            remove.style.margin = "0px";
            remove.style.float = "none";
            remove.style.lineHeight = "0pt";
            remove.title = skinGadget.prefs.getMsg("tabScheduleRemoveHover");
            removeLinkHolder.style.textAlign = "center";
            removeLinkHolder.appendChild(removeLink);
            removeLink.setAttribute("href", "javascript:void(0)");
            removeLink.onclick = function ()
            {
                this.blur();
                skinGadget.deleteRule(row.id);
            };
            removeLink.appendChild(remove);
            var criteria = skinGadget.criteria[conditionIndex][2];
            var conditionValue = criteria == null ? "" : null;
            if (conditionValue == null)
            {
                if (criteria.length == 0)
                {
                    conditionValue = conditionValueSet;
                }
                else
                {
                    conditionValue = skinGadget.criteria[conditionIndex][2][conditionValueIndex];
                }
            }

            //add space at front might be undefined for the catchall
            var space = conditionValue ? " " : "";
            cell1.innerHTML = '<img title = "' + title + '" width="80" height="25" src="' + thumbUrl + '"/>';
            cell2.innerHTML = skinGadget.criteria[conditionIndex][1] + space + (conditionValue || "");
            cell2.setAttribute("title", conditionValue);
            cell3.style.lineHeight = "0pt";
            cell3.style.textAlign = "center";
            cell3.appendChild(removeLinkHolder);
            row.appendChild(cell1);
            row.appendChild(cell2);
            row.appendChild(cell3);
            rules.appendChild(row);

            var table = document.getElementById("cTable" + skinGadget.moduleId);
            var tableDnD = new TableDnD();
            tableDnD.init(table, skinGadget.moduleId);
            tableDnD.onDrop = function(table, row)
            {
                skinGadget.saveRules();
                skinGadget.computeAndSetSkinFromSchedule(skinGadget);
            }

            if (save)
            {
                skinGadget.saveRules();
                skinGadget.computeAndSetSkinFromSchedule(skinGadget);
            }
        }
    };

    this.deleteRule = function(ruleId)
    {
        _gel(ruleId).style.display = "none";
        this.saveRules();
        this.computeAndSetSkinFromSchedule(this);
    };

    this.onGotSkins = function(response, skinGadget)
    {
        if ((response == null) || (response == "") || (response.indexOf("ERROR") >= 0))
        {
            _gel("loading" + skinGadget.moduleId).innerHTML =
            '<a href="javascript:void(0)" onclick="skinGadget' + skinGadget.moduleId
                    + '.getSkins();"><img style="position:relative; top:5px;" src="'
                    + skinGadget.getUrl("http://igskins.googlecode.com/svn/trunk/images/arrow_refresh_small.png")
                    + '" alt="Refresh" title="' + skinGadget.prefs.getMsg('tabDirectoryReload') + '"/></a>'
                    + skinGadget.prefs.getMsg('tabDirectoryLoadingFailed') + ' <a href="' + skinGadget.directoryUrl
                    + '">' + skinGadget.prefs.getMsg('tabDirectoryDirectory') + '</a>.';
        }
        else
        {
            eval("skinGadget.skinData = " + response + ";");

            //add google themes
            if (skinGadget.skinChoice >= 1)
            {
                for (var i = 0; i < skinGadget.googleSkins.length; i++)
                {
                    var newItem = new Object();
                    newItem.name = skinGadget.googleSkins[i][0];
                    newItem.thumbnail = skinGadget.googleSkins[i][1]
                    newItem.url = skinGadget.googleSkins[i][2];
                    newItem.author = "Google";
                    newItem.id = "-" + i;

                    skinGadget.skinData.unshift(newItem);
                }
            }

            //todo memoize all directory queries
            for (var i = 0; i < skinGadget.skinData.length; i++)
            {
                eval("skinGadget.skinDataHash[" + skinGadget.skinData[i].id + "] = skinGadget.skinData[i]");
            }

            var directory = "";
            var skinClass = "";

            if (skinGadget.skinChoice != 1)
            {
                //Add custom skins
                for (var i = 0; i < skinGadget.skinData.length; i++)
                {
                    var item = skinGadget.skinData[i];
                    var thumbUrl = skinGadget.getImageUrl(item.thumbnail);

                    skinClass =
                    ((item.name == skinGadget.skinName) && (!skinGadget.isScheduleMode)) ? "skinthsel" : "skinth";
                    directory +=
                    '<div title="' + item.name + ' by ' + item.author + '" style="overflow:hidden;" class="' + skinClass
                            + '" id="' + item.name + skinGadget.moduleId + '" onclick="skinGadget' + skinGadget.moduleId
                            + '.fetchSkin(\'' + item.url + '\', \'' + item.name + '\');	">';
                    directory += '<img width="80" height="25" src="' + thumbUrl + '"/>';
                    directory += '<b>' + item.name + '</b>';

                    if ((skinGadget.showAuthorUrls) && (item.authorUrl != ""))
                    {
                        directory +=
                        '<a style="text-decoration:none;" onclick="event.cancelBubble=true; this.blur();" title="'
                                + skinGadget.prefs.getMsg("visitAuthor") + '" href="' + item.authorUrl
                                + '">&nbsp;&raquo;</a>';
                    }
                    directory += '</div>';
                }
            }

            skinClass = ((skinGadget.skinName == "") && (!skinGadget.isScheduleMode)) ? "skinthsel" : "skinth";
            var offMsg = (skinGadget.testSkin == "") ? skinGadget.prefs.getMsg("off") :
                         skinGadget.prefs.getMsg("modeTesting");
            var off = '<div class="' + skinClass + '" id="' + skinGadget.testing + skinGadget.moduleId
                    + '" onclick="this.blur(); skinGadget' + skinGadget.moduleId + '.fetchSkin(\'' + skinGadget.testSkin
                    + '\', \'' + skinGadget.testing + '\');" class="skinth"><img width="80" height="25" alt="' + offMsg
                    + '" src="' + skinGadget.getUrl("http://igskins.googlecode.com/svn/trunk/images/classic.png")
                    + '"/><b>' + offMsg + '</b></div>';

            var onclick = skinGadget.showSchedule ?
                          'onclick="skinGadget' + skinGadget.moduleId + '.tabs.setSelectedTab(1);"' : '';
            var link = skinGadget.showSchedule ?
                       '&nbsp;<a style="text-decoration:none;" href="javascript:void(0);" title="Edit schedule" '
                               + onclick + '>&raquo;</a>' : '';

            var sched = "";

            if (skinGadget.showSchedule)
            {
                sched = '<div class="' + skinClass + '" id="scheduleMode' + skinGadget.moduleId
                        + '" onclick="this.blur(); skinGadget' + skinGadget.moduleId
                        + '.computeAndSetSkinFromSchedule(skinGadget' + skinGadget.moduleId
                        + ');" class="skinth"><img width="80" height="25" alt="'
                        + skinGadget.prefs.getMsg("modeSchedule") + '" src="'
                        + skinGadget.getUrl("http://igskins.googlecode.com/svn/trunk/images/schedule.jpg") + '"/><b>'
                        + skinGadget.prefs.getMsg("modeSchedule") + '</b>' + link + '</div>';
            }

            _gel("directory" + skinGadget.moduleId).innerHTML = off + sched + directory;
        }

        if (skinGadget.isScheduleMode)
        {
            _gel("scheduleMode" + skinGadget.moduleId).className = "skinthsel";
        }

        //Finish init
        //Render required tabs here so we have the skin data ready for the schedule tab
        skinGadget.getSchedule(1, skinGadget);

        if (skinGadget.tabs == null)
        {
            skinGadget.renderTabs(skinGadget);

            //Figure out gadget arguments as _args doesn't work for inlined gadges
            if (skinGadget.presetName == "")
            {
                //scrape params from gadgeturl
                if (remote_modules)
                {
                    for (var i = 0; i < remote_modules.length; i++)
                    {
                        var gadget = remote_modules[i];
                        if (gadget.id == skinGadget.moduleId)
                        {
                            var qs = new Querystring(gadget.spec_url.split("?")[1]);
                            skinGadget.skinName = qs.get("up_skinName", skinGadget.skinName);
                            skinGadget.testSkin = qs.get("up_testSkin", skinGadget.testSkin);
                            skinGadget.cssUrl = qs.get("up_cssUrl", skinGadget.cssUrl);
                            skinGadget.schedule = qs.get("up_schedule", skinGadget.schedule);
                            skinGadget.parseBool(qs.get("up_isScheduleMode", skinGadget.isScheduleMode));
                            skinGadget.prefs.set("presetSkinName", skinGadget.skinName);
                            skinGadget.save();

                            break;
                        }
                    }
                }
            }

            if (skinGadget.skinName == "_SCHEDULE")
            {
                skinGadget.computeAndSetSkinFromSchedule(skinGadget);
            }
            else
            {
                if ((skinGadget.skinName.toUpperCase() == skinGadget.testing) || (skinGadget.cssUrl == ""))
                {
                    //set off skin
                    _gel(skinGadget.testing + skinGadget.moduleId).className = "skinthsel";
                }
                skinGadget.fetchSkin(skinGadget.cssUrl, skinGadget.skinName, true);
            }
        }
    };

    this.parseBool = function(s)
    {
        if (!s) return false;
        s = s.toString().toLowerCase();
        return s == "true" || s == "yes" || parseInt(s) > 0;
    }

    this.computeAndSetSkinFromSchedule = function(skinGadget)
    {
        this.cssUrl = "_SCHEDULE";

        var rulesBody = _gel("rules" + skinGadget.moduleId);
        var skinId = null;
        var defaultSkinId;
        var activeRuleId;
        var defaultRuleId;
        var omfgDivs = _gelstn("div");
        for (var i = rulesBody.childNodes.length - 1; i >= 0; i--)
        {
            var row = rulesBody.childNodes[i];
            if (row.style.display != "none")
            {
                row.className = "skinth";
                var ruleSet = row.childNodes[0];
                var ruleSelector = parseInt(ruleSet.id.split("|")[1]);
                switch (ruleSelector)
                        {
                //get default skin
                    case 0 :
                        defaultSkinId = ruleSet.id.split("|")[0];
                        defaultRuleId = row.id;
                        break;

                //until
                    case 1 :
                //after
                    case 2 :
                //dow
                    case 4 :

                        var timeString = ruleSet.nextSibling.title;
                        var now = new Date();
                        var time = new Date();
                        time.setHours(timeString.split(":")[0]);
                        time.setMinutes(timeString.split(":")[1]);

                        if (((ruleSelector == 1) && (now.getTime() < time.getTime())) || ((ruleSelector == 2)
                                && (now.getTime() > time.getTime())) || ((ruleSelector == 4)
                                && (skinGadget.getDoW()[now.getDay()] == ruleSet.nextSibling.title)))
                        {
                            skinId = ruleSet.id.split("|")[0];
                            activeRuleId = row.id;
                        }
                        break;

                //weather
                    case 3:
                //gmail
                    case 5:
                    //todo pubsub for weather? This is too slow
                        var found = false;
                        for (var j = 0; (j < omfgDivs.length) && !found; j++)
                        {
                            var aDiv = omfgDivs[j];
                            if ((ruleSelector == 3) && (aDiv.className == "w_fc"))
                            {
                                found = true;
                                if (aDiv.title.toLowerCase() == ruleSet.nextSibling.title.toLowerCase())
                                {
                                    skinId = ruleSet.id.split("|")[0];
                                    activeRuleId = row.id;
                                }
                            }
                            else if ((ruleSelector == 5) && (aDiv.className == "modlabel"))
                            {
                                var re = new RegExp("GM[0-9]*label");
                                if (aDiv.id.match(re))
                                {
                                    found = true;
                                    try
                                    {
                                        var count = parseInt(aDiv.firstChild.firstChild.firstChild.innerHTML.toLowerCase().replace("inbox (", "").replace(")", ""));
                                        if (count < ruleSet.nextSibling.title)
                                        {
                                            skinId = ruleSet.id.split("|")[0];
                                            activeRuleId = row.id;
                                        }
                                    }
                                    catch(e)
                                    {
                                    }
                                }
                            }
                        }

                        break;
                //ip address
                    case 6:
                        var ipToMatch = ruleSet.nextSibling.title.toLowerCase();
                        if ((ipaddr) && (ipaddr.substring(0, ipToMatch.length) == ipToMatch))
                        {
                            skinId = ruleSet.id.split("|")[0];
                            activeRuleId = row.id
                        }
                        break;
                }
            }
        }
        activeRuleId = activeRuleId || defaultRuleId;
        skinId = skinId || defaultSkinId;
        if (activeRuleId)
        {
            _gel(activeRuleId).className = "skinthsel";
        }

        var saveDiv = _gel("saveDiv" + skinGadget.moduleId);
        if (saveDiv)
        {
            saveDiv.style.display = (skinGadget.cssUrl == this.storedCssUrl) ? "none" : "block";
        }

        if (skinId)
        {
            var scheduleSkinUrl = skinGadget.skinDataHash[skinId].url;
            if (skinGadget.scheduleCss != scheduleSkinUrl)
            {
                skinGadget.scheduleCss = scheduleSkinUrl;
                skinGadget.fetchSkin(scheduleSkinUrl, skinGadget.skinDataHash[skinId].name, false, true);
            }
        }
        else
        {
            skinGadget.scheduleCss = "";
            skinGadget.fetchSkin("", null, false, true);
        }

        _gel("scheduleMode" + this.moduleId).className = "skinthsel";
    };

    this.showAd = function()
    {
        var contentBox = document.createElement("iframe");

        contentBox.frameBorder = 0;
        contentBox.width = '100%';
        contentBox.height = '60px';
        contentBox.scrolling = 'no';
        contentBox.vspace = 0;
        contentBox.hspace = 0;
        contentBox.style.left = 0;
        contentBox.marginWidth = 0;
        contentBox.marginHeight = 0;
        contentBox.style.backgroundColor = "#ffffff";
        contentBox.src = "http://igskins.googlecode.com/svn/trunk/ad.html";

        var ad = this.miniMsg.createStaticMessage(contentBox);
        ad.className = "adClass" + this.moduleId;
    };

    this.submit = function()
    {
        _gel("submitButton" + this.moduleId).disabled = true;

        var skinName = _gel("skinName" + this.moduleId).value;
        var skinAuthor = _gel("skinAuthor" + this.moduleId).value;
        var skinUrl = _gel("skinUrl" + this.moduleId).value;
        var skinAuthorUrl = _gel("skinAuthorUrl" + this.moduleId).value;
        var skinAuthorEmail = _gel("skinAuthorEmail" + this.moduleId).value;

        var url = "http://bonstio.net/igskins/submitSkin.php?" + "skinName=" + skinName + "&" + "skinAuthor="
                + skinAuthor + "&" + "skinAuthorUrl=" + skinAuthorUrl + "&" + "skinAuthorEmail=" + skinAuthorEmail + "&"
                + "skinUrl=" + skinUrl;
        url = encodeURI(url);
        _IG_FetchContent(url, _IG_Callback(this.onsubmitted, this), { refreshInterval: 0 });
    };

    this.onsubmitted = function(response, skinGadget)
    {
        _gel("submitButton" + skinGadget.moduleId).disabled = false;

        var message = "";
        var miniMsg;
        if (response.indexOf("WARNING") >= 0)
        {
            miniMsg =
            skinGadget.miniMsg.createTimerMessage(skinGadget.prefs.getMsg("tabSubmitWarning") + " " + response, 10)
            miniMsg.style.backgroundColor = "orange";
            miniMsg.style.color = "black";
            miniMsg.style.margin = "1px";
        }
        else if ((response.indexOf("ERROR") >= 0) || (response.indexOf("HTML") >= 0))
        {
            miniMsg =
            skinGadget.miniMsg.createTimerMessage(skinGadget.prefs.getMsg("tabSubmitError") + " " + response, 10)
            miniMsg.style.backgroundColor = "red";
            miniMsg.style.color = "white";
            miniMsg.style.margin = "1px";
        }
        else
        {
            miniMsg = skinGadget.miniMsg.createTimerMessage(skinGadget.prefs.getMsg("tabSubmitOK"), 10);
            miniMsg.style.margin = "1px";
        }
    };

    this.addCustomStyle = function(id, cssUrl, atStart)
    {
        var cssNode = document.createElement('link');
        cssNode.type = 'text/css';
        cssNode.rel = 'stylesheet';
        cssNode.href = cssUrl;
        cssNode.media = 'screen';
        cssNode.setAttribute('id', id);

        if (!atStart)
        {
            _gelstn("head")[0].appendChild(cssNode);
        }
        else
        {
            var insertAt = _gelstn("head")[0];
            insertAt.insertBefore(cssNode, insertAt.firstChild);
        }
    };

    this.getUrl = function(url, override)
    {
        if ((this.useCache) || (override))
        {
            //url = _IG_GetCachedUrl(url);
            url = "http://gmodules.com/ig/proxy?url=" + encodeURIComponent(url);
        }

        return url;
    };

    this.getImageUrl = function(url)
    {
        if (this.useCache)
        {
            url = _IG_GetImageUrl(url);
        }

        return url;
    };

    this.setTitle = function(skinName, isScheduled)
    {
        //Get the title
        var title = skinName;
        if ((!title) || (title == "") || (title.toUpperCase() == this.testing))
        {
            if ((this.testSkin != "") && (!isScheduled))
            {
                title = "Testing";
            }
            else
            {
                title = "Off";
            }
        }
        else if (isScheduled)
        {
            title += " [s]"
        }

        _IG_SetTitle("Custom Skins : " + title, this.moduleId);
    };

    this.fetchSkin = function(cssUrl, skinName, init, isScheduled)
    {
        this.isDirty = false;
        isScheduled = isScheduled != undefined;
        this.isScheduleMode = isScheduled;
        //Figure and set title
        if (skinName == this.feelingLucky)
        {
            _IG_SetTitle(this.prefs.getMsg("flickrLoading"), this.moduleId);
            this.loadingImage = true;
            this.addLoader(this);
        }
        else
        {
            this.removeLoader(this);
            this.setTitle(skinName, isScheduled);
        }

        //unless initializing, check for dirty
        if ((init == undefined) || (!init))
        {
            //has the skin changed?
            if ((skinName) && (skinName == this.skinName))
            {
                if (skinName == this.feelingLucky)
                {
                    this.getFlickrPhoto(this);
                }
                return;
            }
        }


        if (skinName != this.feelingLucky)
        {
            this.removeRandomBodyImage(this);
        }

        //Sort the save button if not a scheduled change
        //turn off last skin in UI
        _gel("scheduleMode" + this.moduleId).className = "skinth";
        _gel(this.testing + this.moduleId).className = "skinth";


        var current = _gel(this.skinName + this.moduleId);
        if (current)
        {
            current.className = "skinth";
        }

        if (!isScheduled)
        {
            this.scheduleCss = "";
            //_gel('save' + this.moduleId).disabled = (skinName == this.skinName);
            var saveDiv = _gel("saveDiv" + this.moduleId);
            if (saveDiv)
            {
                saveDiv.style.display = (cssUrl == this.storedCssUrl) ? "none" : "block";
            }
            //set the new one
            var newSkin = _gel((skinName || this.testing) + this.moduleId);
            if (newSkin)
            {
                newSkin.className = "skinthsel";
            }
        }

        if ((skinName) && (skinName.toUpperCase() == this.testing) && (this.testSkin != ""))
        {
            this.useCache = false;
            cssUrl = "";
            _IG_FetchContent(this.testSkin, _IG_Callback(this.setSkin, this), { refreshInterval: 0 });
        }
        else if (!cssUrl)
        {
            var extCss = _gel("ext_css");
            if (extCss)
            {
                //delete or replace style custom sheet
                if (this.origCss != "")
                {
                    extCss.setAttribute("href", this.origCss);
                }
            }
        }
        else
        {
            //fetch and inspect a remote skin
            this.useCache = true;
            _IG_FetchContent(cssUrl, _IG_Callback(this.setSkin, this, isScheduled ? cssUrl : null), { refreshInterval: 60 });
        }

        //Store the cssUrl - the non-cached version
        if (isScheduled)
        {
            cssUrl = "";
            skinName = "_SCHEDULE";
            cssUrl = "_SCHEDULE";
        }
        this.cssUrl = cssUrl ? cssUrl : "";
        this.skinName = skinName ? skinName : this.testing;

    };

    this.isSafeBeta = function(response, skinName)
    {
        //todo fixup
        response = response.toLowerCase();
        return !((response.indexOf("@import") >= 0) || (response.indexOf(".js") >= 0) || (response.indexOf(".swf") >= 0)
                || (response.indexOf(".vbs") >= 0) || (response.indexOf("<link") >= 0));
    };

    this.setSkin = function(response, skinGadget, scheduleSkinUrl)
    {
        if ((response) && (response != ""))
        {
            if (!skinGadget.isSafeBeta(response, skinGadget.skinName))
            {
                //blacklist
                alert(skinGadget.skinName
                        + " skin - critical error: failed to render skin - could not be found or rendered. Please report this to skins@bonstio.net along with the URL "
                        + skinGadget.cssUrl);
                return;
            }
        }

        //Look for external stylesheet
        var extCss = _gel("ext_css");

        //Use the cache unless testing
        var cssUrl = skinGadget.getUrl(scheduleSkinUrl || skinGadget.cssUrl);
        cssUrl = skinGadget.skinName.toUpperCase() == skinGadget.testing ? skinGadget.testSkin : cssUrl;

        //simplest case: replace css with external one
        if ((extCss) && (cssUrl))
        {
            extCss.parentNode.removeChild(extCss);
            skinGadget.addCustomStyle("ext_css", cssUrl);
        }
        else if ((!extCss) && (cssUrl))
        {
            skinGadget.addCustomStyle("ext_css", cssUrl);
        }

        if ((scheduleSkinUrl || skinGadget.cssUrl) == skinGadget.feelingLuckyUrl)
        {
            skinGadget.getFlickrPhoto(skinGadget);
        }


        if ((skinGadget.adSupported == 1) && (skinGadget.skinChanges++ == 4))
        {
            skinGadget.showAd();
        }
    };

    this.getFlickrPhoto = function(skinGadget)
    {
        if (skinGadget.flickrGroupId != "")
        {
            if (skinGadget.flickrResponse != null)
            {
                skinGadget.onGotFlickrGroup(skinGadget.flickrResponse, skinGadget);
            }
            else
            {
                var url = "http://api.flickr.com/services/rest/?" + "method=" + _esc("flickr.photos.search")
                        + "&format=rest" + "&api_key=" + _esc("d72f4291ca0015e3c71ddd64af5b154f") + "&extras="
                        + _esc("original_format") + "&per_page=500" + "&group_id=" + _esc(skinGadget.flickrGroupId);
                _IG_FetchXmlContent(url, _IG_Callback(skinGadget.onGotFlickrGroup, skinGadget));
            }
        }
    }

    this.optOut = function()
    {
        this.prefs.set("showUpdates", false);
    };

    this.scoreUsage = function()
    {
        //Log current skin for usage stats. This cannot really be cached otherwise my db won't update
        _IG_FetchContent(encodeURI("http://bonstio.net/igskins/currentSkin.php?name=" + this.skinName), function(
                response)
        {
        }, { refreshInterval: 0 });
    };

    this.showFilter = function(show)
    {
        if (show)
        {
            _gel("filterBox" + this.moduleId).style.display = "block";
            _gel("filterString" + this.moduleId).focus();
            this.filter();
        }
        else
        {
            _gel("filterString" + this.moduleId).blur();
            _gel("filterBox" + this.moduleId).style.display = "none";
        }
    };

    this.filter = function()
    {
        var saveCommand = "skinGadget" + this.moduleId + ".doFilter(" + ++this.saveId + ");";
        window.setTimeout(saveCommand, 250);
    };

    this.doFilter = function(id)
    {
        if ((id) && (id < this.saveId))
        {
            return;
        }

        var filterString = _gel("filterString" + this.moduleId).value;
        filterString = filterString.toLowerCase();

        var directory = _gel("directory" + this.moduleId);
        for (var i = 0, limit = directory.childNodes.length; i < limit; i++)
        {
            var skinDiv = directory.childNodes[i];
            if (skinDiv.title.toLowerCase().indexOf(filterString) < 0)
            {
                skinDiv.style.display = "none";
            }
            else
            {
                skinDiv.style.display = "";
            }
        }
    };

    this.save = function()
    {
        var saveCommand = "skinGadget" + this.moduleId + ".setPrefs(" + ++this.saveId + ");";
        window.setTimeout(saveCommand, 1);
        //do it now - i.e. 1
        //_gel("save" + this.moduleId).disabled = true;
        var saveDiv = _gel("saveDiv" + this.moduleId);
        if (saveDiv)
        {
            saveDiv.style.display = "none";
        }
    };

    this.setPrefs = function(save)
    {
        if ((save) && (save < this.saveId))
        {
            return;
        }

        this.prefs.set("cssUrl", encodeURIComponent(this.cssUrl), "skinName", this.skinName, "schedule", encodeURIComponent(this.schedule), "isScheduleMode", this.isScheduleMode);
        this.isDirty = false;
        //if dirty
        if (this.cssUrl != this.storedCssUrl)
        {
            this.storedCssUrl = this.cssUrl;
            this.scoreUsage();
        }
    };

    this.getHours = function()
    {
        var hours = new Array();
        var time = "";
        for (var i = 0; i < 24; i++)
        {
            time = i + ":00";
            while (time.length < 5)
            {
                time = "0" + time;
            }
            hours[hours.length] = time;

            time = i + ":30";
            while (time.length < 5)
            {
                time = "0" + time;
            }
            hours[hours.length] = time;
        }

        return hours;
    }

    this.getDoW = function()
    {
        return ["Sunday", "Monday","Tuesday", "Wednesday","Thursday", "Friday","Saturday"];
    }

    this.getHoursOfDay = function (rounding)
    {
        var now = new Date();
        var hours = now.getHours();
        var ampm = "am";
        if (hours > 12)
        {
            hours -= 12;
            ampm = "pm";
        }
        else if (hours == 0)
        {
            hours = 12;
        }

        hours = Math.round(hours / rounding) * rounding;

        return hours + ampm;
    }

    this.getTabId = function(tabName, skinGadget)
    {
        var tabs = skinGadget.tabs.getTabs();
        for (var i = 0; i < tabs.length; i++)
        {
            if (tabs[i].getName() == tabName)
            {
                return i;
            }
        }
        return -1;
    }

    this.removeRandomBodyImage = function(skinGadget)
    {
        var imgStyle = _gel("randomImage" + skinGadget.moduleId);
        if (imgStyle)
        {
            imgStyle.parentNode.removeChild(imgStyle);
        }
    }

    this.onGotFlickrGroup = function(response, skinGadget)
    {

        if (response == null || typeof(response) != "object" || response.firstChild == null)
        {
            return;
        }

        var photos = response.getElementsByTagName("photos")[0];
        if ((photos) && (photos.childNodes.length > 0))
        {
            var rand = Math.floor(Math.random() * photos.childNodes.length);
            while (((!photos.childNodes[rand].attributes)
                    || (photos.childNodes[rand].attributes.getNamedItem("originalsecret") == null)
                    ) && (rand > 0))
            {
                rand--;
            }
            var pA = photos.childNodes[rand].attributes;
            if (pA)
            {
                var imgUrl = "http://farm" + pA.getNamedItem("farm").value + ".static.flickr.com/"
                        + pA.getNamedItem("server").value + "/" + pA.getNamedItem("id").value + "_"
                        + pA.getNamedItem("originalsecret").value + "_o." + pA.getNamedItem("originalformat").value;

                skinGadget.bgImage.onload = function()
                {
                    skinGadget.loadingImage = false;
                    skinGadget.removeLoader(skinGadget);
                    if ((skinGadget.skinName == skinGadget.feelingLucky) || (skinGadget.scheduleCss == skinGadget.feelingLuckyUrl))
                    {
                        skinGadget.setTitle(skinGadget.feelingLucky, skinGadget.isScheduleMode);
                        skinGadget.removeRandomBodyImage(skinGadget);
                        skinGadget.addRandomBodyImage(skinGadget.bgImage.src, skinGadget);
                    }
                };
                skinGadget.bgImage.src = imgUrl;

                if (skinGadget.flickrResponse == null)
                {
                    //hang on to response for next time
                    skinGadget.flickrResponse = response;
                }
            }
        }
    }

    this.removeLoader = function(skinGadget)
    {
        var loader = _gel("ajaxLoader" + skinGadget.moduleId);
        if (loader)
        {
            loader.parentNode.removeChild(loader);
        }
        var randomSkin = _gel(skinGadget.feelingLucky + skinGadget.moduleId);
        if (randomSkin)
        {
            randomSkin.style.cursor = "pointer";
        }
    }

    this.addLoader = function(skinGadget)
    {
        if (!_gel("ajaxLoader" + skinGadget.moduleId))
        {
            var gadget = _gel("m_" + moduleId);
            var container = gadget.style.display == "none" ? _gel("sSpan" + skinGadget.moduleId) : _gel("m_" + skinGadget.moduleId + "_h");
            if (container)
            {
                var loader = new Image();
                loader.src = skinGadget.getUrl("http://igskins.googlecode.com/svn/trunk/images/ajax-loader.gif");
                loader.setAttribute("id", "ajaxLoader" + skinGadget.moduleId);
                loader.setAttribute("title", skinGadget.prefs.getMsg("flickrLoadingToolTip"));
                loader.style.verticalAlign = "middle";
                loader.style.paddingLeft = "2px";
                loader.style.cursor = "help";

                if (gadget.style.display == "none")
                {
                    container.insertBefore(loader, container.firstChild.nextSibling);
                }
                else
                {
                    container.appendChild(loader);
                }
            }

            if (!skinGadget.isScheduleMode)
            {
                var randomSkin = _gel(skinGadget.feelingLucky + skinGadget.moduleId);
                if (randomSkin)
                {
                    randomSkin.style.cursor = "wait";
                }
            }
        }
    }

    this.addRandomBodyImage = function(url, skinGadget)
    {
        var ua = navigator.userAgent.toLowerCase();
        var isIE = (/msie/.test(ua)) && !(/opera/.test(ua)) && (/win/.test(ua));
        var style_node = document.createElement("style");
        style_node.setAttribute("type", "text/css");
        style_node.setAttribute("media", "screen");
        style_node.setAttribute("id", "randomImage" + skinGadget.moduleId);
        var selector = "body";
        var declaration = "background: #222 url( '" + url + "' ) repeat top center";
        if (!isIE) style_node.appendChild(document.createTextNode(selector + " { " + declaration + " }"));
        document.getElementsByTagName("head")[0].appendChild(style_node);
        if (isIE && document.styleSheets && document.styleSheets.length > 0)
        {
            var last_style_node = document.styleSheets[document.styleSheets.length - 1];
            if (typeof(last_style_node.addRule) == "object") last_style_node.addRule(selector, declaration);
        }
    }

    this.toggleHidden = function(e)
    {
        e = e || window.event;
        if (e.target)
        {
            e.target.blur();
        }
        else if (e.which)
        {
            e.which.blur();
        }

        var gadget = _gel("m_" + this.moduleId);
        gadget.style.display = gadget.style.display == "none" ? "block" : "none";
        this.removeLoader(this);
        if (this.loadingImage)
        {
            this.addLoader(this);
        }

    }

    this.createMethodReference = function(object, methodName)
    {
        return function ()
        {
            object[methodName].apply(object, arguments);
        };
    }

    this.init();
}
;
